﻿using System.Data.Entity;
using System.Collections.Generic;
using System.Web.Security;
using System;
using System.Linq;
using System.Data.Objects;
using System.Data.Entity.Infrastructure;
using System.Data;
using System.Data.Common;
using System.Web;

namespace EventPlanner.Models
{
    /// <summary>
    /// Acceso a datos sobre entidades de EventPlanner
    /// </summary>    
    public class EventPlannerContext : DbContext, EventPlanner.Models.IEventPlannerContext
    {
        public IDbSet<Evento> Evento { get; set; }
        public IDbSet<Organizador> Organizador { get; set; }

        public IDbSet<Participante> Participantes { get; set; }

        public IDbSet<Contacto> Contactoes { get; set; }

        public IDbSet<AuditInfo> AuditInfo { get; set; }


        public int SetModified(EntityBase entity)
        {
            try
            {
                Entry(entity).State = EntityState.Modified;
                return 0;
            }
            catch (Exception e)
            {
                return -1;
            }
        }

        /// <summary>
        /// Obtener un evento
        /// </summary>
        /// <param name="id">id de evento a obtener</param>
        /// <returns>Evento encontrado</returns>
        public Evento GetEvento(int id)
        {
            try
            {
                Evento evento = Evento.Find(id);
                if (evento == null)
                {
                    return new Evento() { id = EntityBase.ID_NO_EXISTE };
                }
                else return evento;
            }
            catch
            {
                return new Evento() { id = EntityBase.ID_NO_EXISTE };
            }
        }

        /// <summary>
        /// Obtener un organizador
        /// </summary>
        /// <param name="id">id de organizador a obtener</param>
        /// <returns>organizador encontrado</returns>
        public Organizador GetOrganizador(int id)
        {
            try
            {
                Organizador organizador = Organizador.Find(id);
                if (organizador == null)
                {
                    return new Organizador() { id = EntityBase.ID_NO_EXISTE };
                }
                else return organizador;
            }
            catch
            {
                return new Organizador() { id = EntityBase.ID_NO_EXISTE };
            }
        }

        /// <summary>
        /// Obtener un participante
        /// </summary>
        /// <param name="id">id de participante a obtener</param>
        /// <returns>Participante encontrado</returns>
        public Participante GetParticipante(int id)
        {
            try
            {
                Participante participante = Participantes.Find(id);
                if (participante == null)
                {
                    return new Participante() { id = EntityBase.ID_NO_EXISTE };
                }
                else return participante;
            }
            catch
            {
                return new Participante() { id = EntityBase.ID_NO_EXISTE };
            }
        }

        /// <summary>
        /// Obtener un contacto
        /// </summary>
        /// <param name="id">id de contacto a obtener</param>
        /// <returns>Contacto encontrado</returns>
        public Contacto GetContacto(int id)
        {
            try
            {
                Contacto contacto = Contactoes.Find(id);
                if (contacto == null)
                {
                    return new Contacto() { id = EntityBase.ID_NO_EXISTE };
                }
                else return contacto;
            }
            catch
            {
                return new Contacto() { id = EntityBase.ID_NO_EXISTE };
            }
        }

        #region Auditing

        protected void WriteAuditInfo(AuditInfo auditInfo)
        {
            AuditInfo.Add(auditInfo);
            base.SaveChanges();
        }

        /// <summary>
        /// Obtener ultimo registro de audit con flag de ultima actualizacion en true
        /// </summary>
        /// <param name="EntityDbId">Id de entidad en base de datos</param>
        /// <returns>LastRecordFlag = true para el tipo de entidad con el id indicado</returns>       
        protected AuditInfo GetLastAuditInfo(int EntityDbId, string EntityType)
        {
            AuditInfo auditInfo = (from audit in AuditInfo
                                   where audit.LastRecordFlag == true && audit.EntityDbId == EntityDbId && audit.EntityType.Equals(EntityType)
                                   select audit).FirstOrDefault() ?? new AuditInfo() { id = EntityBase.ID_NO_EXISTE };
            return auditInfo;
        }

        public override int SaveChanges()
        {
            ChangeTracker.DetectChanges();
            ObjectContext ctx = ((IObjectContextAdapter)this).ObjectContext;
            List<ObjectStateEntry> objectStateEntryList =
                ctx.ObjectStateManager.GetObjectStateEntries(EntityState.Added
                                                           | EntityState.Modified
                                                           | EntityState.Deleted)
                .ToList();
            // -- Registrar inserciones
            var AuditInsertedList = new List<AuditInfo>();
            var EntryInsertedList = new List<ObjectStateEntry>();
            foreach (ObjectStateEntry entry in objectStateEntryList)
            {
                if (!entry.IsRelationship)
                {
                    string EntityType = ((EntityBase)entry.Entity).GetEntityType();
                    switch (entry.State)
                    {
                        case EntityState.Added: // -- INSERTAR
                            AuditInfo insertedAuditInfo = new AuditInfo()
                            {
                                EntityAction = EventPlanner.Resources.Global.Codigo.str_INSERTAR,
                                EntityCreatedDate = DateTime.Now,
                                EntityUser = HttpContext.Current.User.Identity.Name,
                                EntityName = entry.CurrentValues.GetValue(1).ToString(),
                                EntityType = EntityType,
                                LastRecordFlag = true
                            };
                            AuditInsertedList.Add(insertedAuditInfo);
                            EntryInsertedList.Add(entry);
                            break;
                        case EntityState.Deleted: // -- ELIMINAR
                            AuditInfo DeleteNewAuditInfo = new AuditInfo()
                            {
                                EntityAction = EventPlanner.Resources.Global.Codigo.str_ELIMINAR,
                                EntityDbId = (int)entry.OriginalValues.GetValue(entry.OriginalValues.GetOrdinal("id")),
                                EntityDeletedDate = DateTime.Now,
                                EntityUser = HttpContext.Current.User.Identity.Name,
                                EntityName = entry.OriginalValues.GetValue(1).ToString(),
                                EntityType = EntityType,
                                LastRecordFlag = true
                            };
                            AuditInfo DeleteLastAuditInfo = GetLastAuditInfo(DeleteNewAuditInfo.EntityDbId, DeleteNewAuditInfo.EntityType);
                            if (DeleteLastAuditInfo.IsValid())
                            {
                                DeleteNewAuditInfo.EntityUpdatedDate = DeleteLastAuditInfo.EntityUpdatedDate;
                                DeleteNewAuditInfo.EntityCreatedDate = DeleteLastAuditInfo.EntityCreatedDate;
                                DeleteNewAuditInfo.EntityName = DeleteLastAuditInfo.EntityName;
                                DeleteNewAuditInfo.EntityType = DeleteLastAuditInfo.EntityType;
                                DeleteNewAuditInfo.EntityDbId = DeleteLastAuditInfo.EntityDbId;
                                Entry(DeleteLastAuditInfo).State = EntityState.Modified;
                                DeleteLastAuditInfo.LastRecordFlag = false;
                            }
                            AuditInfo.Add(DeleteNewAuditInfo);
                            break;
                        case EntityState.Modified: // EDITAR
                            {
                                AuditInfo EditNewAuditInfo = new AuditInfo()
                                {
                                    EntityAction = EventPlanner.Resources.Global.Codigo.str_EDITAR,
                                    EntityDbId = (int)entry.CurrentValues.GetValue(entry.CurrentValues.GetOrdinal("id")),
                                    EntityUpdatedDate = DateTime.Now,
                                    EntityUser = HttpContext.Current.User.Identity.Name,
                                    EntityName = entry.CurrentValues.GetValue(1).ToString(),
                                    EntityType = EntityType,
                                    LastRecordFlag = true
                                };
                                AuditInfo EditLastAuditInfo = GetLastAuditInfo(EditNewAuditInfo.EntityDbId, EntityType);
                                if (EditLastAuditInfo.IsValid())
                                {
                                    EditNewAuditInfo.EntityCreatedDate = EditLastAuditInfo.EntityCreatedDate;
                                    EditNewAuditInfo.EntityDbId = EditLastAuditInfo.EntityDbId;
                                    this.Entry(EditLastAuditInfo).State = EntityState.Modified;
                                    EditLastAuditInfo.LastRecordFlag = false;
                                }
                                AuditInfo.Add(EditNewAuditInfo);
                                break;
                            }
                    }
                }
            }
            int result = base.SaveChanges();
            // -- Guardar inserciones ya conocido EntityDbID = id
            if (AuditInsertedList.Count > 0)
            {
                var InsertedAuditEnum = AuditInsertedList.GetEnumerator();
                InsertedAuditEnum.MoveNext();
                foreach (ObjectStateEntry entry in EntryInsertedList)
                {
                    InsertedAuditEnum.Current.EntityDbId = (int)entry.CurrentValues.GetValue(entry.CurrentValues.GetOrdinal("id"));
                    WriteAuditInfo(InsertedAuditEnum.Current);
                    InsertedAuditEnum.MoveNext();
                }
            }
            return result;
        }
        #endregion

        public string GetUsuarioActual()
        {
            return HttpContext.Current.User.Identity.Name;
        }

        public string GetMembershipInfo(string atributo)
        {
            if (atributo.ToUpper().Equals("EMAIL"))
                return Membership.GetUser().Email;
            else
                throw new Exception("Autenticación: se intentó obtener atributo no reconocido sobre Membership");
        }



    }





    public class Inicializador : DropCreateDatabaseIfModelChanges<EventPlannerContext>
    {

        protected override void Seed(EventPlannerContext context)
        {
            string[] nombres = { 
                                    "Barrett","Dorene","Hilary","Vivan","Ronnie","Jospeh","Luanna","Echo",
                                    "Latoya","Corrin","Elida", "Delpha","Jannette","Ned","Lenard","Wm",
                                    "Tabitha","Laquita","Fannie","Angeles","Willis","Emmett","Gladys",
                                    "Lyla","Alonso","Caprice","Napoleon","Kareen","Elva","Mckinley"
                                };
            string[] lugares =  { 
                                    "Aide", "Fredric","Collin","Lyle","Alda","Aretha","Christinia","Ingeborg",
                                    "Marjory","Shira","Travis","France","Nicky","Beau","Lanelle","Carin",
                                    "Rufus","Amalia","Sharice","Camille" ,"Adah","Fidel","Tess","Vern",
                                    "Tom","Dodie","Sarai","Monet","Raelene","Lyle"
                                 };
            string[] organizadores = { "Xavier", "Charlie", "Ramon", "Jimmie", "Stuart", "Gretchen", "Leeanne", "Lucas", "Rona", "Martin" };
            string[] publishers = Roles.GetUsersInRole("Publisher");
            Random r = new Random();
            for (int j = 0; j < publishers.Length; j++)
            {
                for (int h = j; h < j + 2; h++)
                {
                    Organizador o = new Organizador();
                    o.Activa = true;
                    o.Creador = publishers[j];
                    o.Nombre = organizadores[r.Next(0, organizadores.Length)];
                    o.id = h;
                    context.Organizador.Add(o);
                }
            }
            context.SaveChanges();
            for (int i = 0; i < 30; i++)
            {
                Evento e = new Evento();
                e.Nombre = nombres[i];
                e.Lugar = lugares[i];
                e.Creador = publishers[r.Next(0, publishers.Length)];
                Organizador[] orgs = (from o in context.Organizador where o.Creador.Equals(e.Creador) select o).ToArray<Organizador>();
                e.OrganizadorId = orgs[r.Next(0, orgs.Length)].id;
                e.Cupo = r.Next(10, 200);
                e.Descripcion =
                    "Lorem ipsum dolor sit amet, Brenton Gilstrap consectetur adipiscing elit. " +
                    "Ut blandit viverra diam luctus luctus. In Evan Vialpando tellus nunc, dapibus id gravida vel, lacinia venenatis augue. " +
                    "Nunc Marlin Baldonado sagittis rhoncus hendrerit. Sed vel augue nisi, vel sagittis sem. " +
                    "Joe Nisbett Aenean ante diam, rutrum ut eleifend in, convallis sed est.";
                int h = r.Next(0, 24);
                int m = r.Next(0, 60);
                e.Fecha = DateTime.Today.Add(new TimeSpan(r.Next(1, 80), h, m, 0));
                e.Hora = e.Fecha;
                e.id = i;
                context.Evento.Add(e);
            }
            context.SaveChanges();
        }
    }
}